package il.ac.tau.cg.ex3.parser.parametershander;

import il.ac.tau.cg.ex3.entities.scene.Camera;
import il.ac.tau.cg.ex3.entities.scene.Scene;
import il.ac.tau.cg.ex3.math.Point;
import il.ac.tau.cg.ex3.math.Vector;
import il.ac.tau.cg.ex3.parser.Parser;
import il.ac.tau.cg.ex3.parser.Parser.ParseException;

public class CameraParametersHandler extends EntityParametersHandler 
{
	private boolean hasDirection; 
	private boolean hasLookAt;
	
	private Point position;
	private Point lookAt;
	private Vector viewDirection;
	private Vector upDirection; 
	
	private double viewPlaneDist;
	private double viewPlaneWidth;
	
	public CameraParametersHandler()
	{
		super();
		
		hasDirection = false; 
		hasLookAt    = false;
		
		position      = null;
		lookAt        = null;
		viewDirection = null;
		upDirection   = null; 
		
		viewPlaneDist  = 0.0;
		viewPlaneWidth = 2.0;
	}
	
	public boolean addParam(String key, String[] value) throws ParseException
	{
		paramsCache.add(key);
		
		ECameraParameters param =  ECameraParameters.findParam(key);
		
		if (param == null)
		{
			return false;
		}
		
		Parser.pAssert((value.length == param.argsNum), param.name + " INVALID ARGS NUM");
		
		switch (param)
		{		
			case EYE:
			{	
				position = getPoint(value);
			} break;

			case DIRECTION:
			{
				hasDirection = true;
				
				viewDirection = getVector(value);
			} break;
				
			case LOOK_AT:
			{
				hasLookAt = true;
				
				lookAt = getPoint(value);
			} break;	
			
			case SCREEN_DIST:
			{
				viewPlaneDist = Double.parseDouble(value[0]);
			} break;
			
			case SCREEN_WIDTH:
			{
				viewPlaneWidth = Double.parseDouble(value[0]);
			} break;
			
			case UP_DIRECTION:
			{
				upDirection = getVector(value);
			} break;
		}
		
		return true;	
	}
	
	protected void createEntity(Scene scene) throws ParseException
	{
		Parser.pAssert(hasLookAt || hasDirection, ECameraParameters.EYE.name + " and " + ECameraParameters.LOOK_AT.name + " ARE MISSING");
		
		Camera camera = null;
		
		if (hasDirection)
		{
			camera = new Camera(position, viewDirection, upDirection, viewPlaneDist, viewPlaneWidth);
		}
		else // hasLookAt
		{
			camera = new Camera(position, lookAt, upDirection, viewPlaneDist, viewPlaneWidth);
		}
		
		scene.setCamera(camera);
	}

	@Override
	protected boolean validateEntity() throws ParseException
	{
		for (ECameraParameters param : ECameraParameters.values())
		{
			if (param.isMandatory && !paramsCache.contains(param.name))
			{
				return false;
			}
		}
		
		return true;
	}

	public enum ECameraParameters
	{
		/* CAMERA ENTITIES */
		EYE           ("eye", true, 3),
		DIRECTION     ("direction", false, 3),
		LOOK_AT       ("look-at", false, 3),
		UP_DIRECTION  ("up-direction", true, 3),
		SCREEN_DIST   ("screen-dist", true, 1),
		SCREEN_WIDTH  ("screen-width", false, 1);
		
		private final String  name;
		private final boolean isMandatory;
		private final int     argsNum;
		
		ECameraParameters(String name, boolean isMandatory, int argsNum)
		{
			this.name = name;
			this.isMandatory = isMandatory;
			this.argsNum = argsNum;
		}
		
		public static ECameraParameters findParam(String name)
		{
			for (ECameraParameters param : ECameraParameters.values())
			{
				if (param.name.equals(name))
				{
					return param;
				}
			}
			
			return null;
		}
	}
}